
#include <BFont>
#include <BPainter>

#include "member_BProgressBar.h"
#include "member_BObject.h"
#include "member_BStyle.h"

using namespace BWE;

#define member					(*(member_BProgressBar*)_ptr)
#define member_allocate()		_ptr = new member_BProgressBar(this)
#define member_release()		delete (member_BProgressBar*)_ptr

BProgressBar::BProgressBar(BWidget* parent) : BWidget(parent)
{
	member_allocate();
}
BProgressBar::BProgressBar(const BString& format, BWidget* parent) : BWidget(parent)
{
	member_allocate();
	member.format = format;
}
BProgressBar::BProgressBar(Orientation orientation, BWidget* parent) : BWidget(parent)
{
	member_allocate();
	member.orientation = orientation;
}
BProgressBar::BProgressBar(const BString& format, Orientation orientation, BWidget* parent) : BWidget(parent)
{
	member_allocate();
	member.orientation = orientation;
	member.format = format;
}
BProgressBar::~BProgressBar()
{
	member_release();
}

void BProgressBar::setAlign(Align align)
{
	if (member.ia_align != align)
	{
		member.ia_align = align;
		member.dirty = true;
		if (this->checks(Signal_AlignChanged))
			emit(Signal_AlignChanged, align);
		this->fresh();
	}
}
Align BProgressBar::align() const
{
	return member.ia_align;
}

void BProgressBar::setFormat(const BString& format)
{
	if (member.format != format)
	{
		member.format = format;
		member.dirty = true;
		object_member(this)->check_emit(Signal_FormatChanged, format);
		this->fresh();
	}
}
const BString& BProgressBar::format() const
{
	return member.format;
}

const BString& BProgressBar::text() const
{
	return member.text;
}

void BProgressBar::setOrientation(Orientation orientation)
{
	if (member.orientation != orientation)
	{
		member.orientation = orientation;
		member.dirty = true;
		this->emit(Signal_OrientationChanged, orientation);
		this->fresh();
	}
}
Orientation BProgressBar::orientation() const
{
	return member.orientation;
}

void BProgressBar::setValue(int value)
{
	value = member.range.limit(value);
	if (member.value != value)
	{
		member.value = value;
		member.dirty = true;
		emit(Signal_ValueChanged, member.value);
		this->fresh();
	}
}
int BProgressBar::value() const
{
	return member.value;
}

void BProgressBar::setStep(int step)
{
	if (step < 0)
		return;
	if (member.step != step)
	{
		member.step = step;
		emit(Signal_StepChanged, member.step);
		this->fresh();
	}
}
int BProgressBar::step() const
{
	return member.step;
}

void BProgressBar::setMinimum(int minimum)
{
	setRange(BRange(minimum, member.range.max()));
}
int BProgressBar::minimum() const
{
	return member.range.min();
}

void BProgressBar::setMaximum(int maximum)
{
	setRange(BRange(member.range.min(), maximum));
}
int BProgressBar::maximum() const
{
	return member.range.max();
}

void BProgressBar::setRange(int minimum, int maximum)
{
	setRange(BRange(minimum, maximum));
}
void BProgressBar::setRange(const BRange& range)
{
	if (member.range != range)
	{
		member.range = range;
		member.dirty = true;
		emit(Signal_RangeChanged, member.range);
		this->fresh();
	}
}
const BRange& BProgressBar::range() const
{
	return member.range;
}

void BProgressBar::increase()
{
	int value = member.value + member.step;
	this->setValue(value);
}
void BProgressBar::decrease()
{
	int value = member.value - member.step;
	this->setValue(value);
}

BReal BProgressBar::ratio() const
{
	return (BReal)(member.value - member.range.min()) / (member.range.size());
}

const BRect& BProgressBar::chunkRect() const
{
	return member.chunkRect;
}
const BRect& BProgressBar::textRect() const
{
	return member.ia_textRect;
}

BSize BProgressBar::sizeHint() const
{
	if (member.dirty)
	{
		member.freshRects();
		member.dirty = false;
	}
	BSize baseSize = member.contentSize;
	if (member.orientation & Orientation_Horizontal)
		return this->preferSize(baseSize.width() * 10, baseSize.height());
	if (member.orientation & Orientation_Vertical)
		return this->preferSize(baseSize.width(), baseSize.height() * 10);
	return this->preferSize(baseSize);
}

bool BProgressBar::query(const BString& text, BValue& value) const
{
	if (text == "text-rect")
	{
		value = member.ia_textRect;
		return true;
	}
	if (text == "chunk-rect")
	{
		value = member.chunkRect;
		return true;
	}
	if (text == "chunk-points")
	{
		BPointfArray& points = value.fill<BPointfArray>();
		points = member.chunkPoints;
		return true;
	}
	return BWidget::query(text, value);
}

void BProgressBar::resizeEvent(const BEvent& event)
{
	member.dirty = true;
	this->fresh();
	BWidget::resizeEvent(event);
}

void BProgressBar::styleEvent(const BEvent& event)
{
	BWidget::styleEvent(event);
	const BStyle* style = event.value();
	style_member(style)->assign(Value_Format, member.format);
	style_member(style)->assign(Value_Chunk_Size, member.chunkSize);
	style_member(style)->assign(Value_Orientation, member.orientation);
	member.dirty = true;
}
void BProgressBar::freshEvent(const BEvent& event)
{
	if (member.dirty)
	{
		member.freshRects();
		member.dirty = false;
	}
	BWidget::freshEvent(event);
}
void BProgressBar::paintEvent(const BEvent& event)
{
	BPainter painter(this);
	painter.drawBackground();
	painter.drawForeground();

	State state = painter.state();

	if (const BImage* image = painter.image(Image_Chunk, state))
	{
		int margin = bMin(image->width(), member.chunkRect.height()) / 2;
		if (member.orientation & Orientation_Horizontal)
		{
			painter.drawImage(member.chunkRect, image, BMargin(margin, 0, margin, 0));
		}
		if (member.orientation & Orientation_Vertical)
		{
			painter.drawImage(member.chunkRect, image, BMargin(0, margin, 0, margin));
		}
	}
	else if (painter.setColor(Color_Chunk, state))
	{
		if (member.chunkPoints.empty())
		{
			painter.fillRect(member.chunkRect);
		}
		else
		{
			painter.setPolySmooth(true);
			painter.fillTriangleFan(member.chunkPoints);
			painter.setPolySmooth(false);
		}
	}

	if (painter.setColor(Color_Text, state))
	{
		painter.drawText(member.ia_textRect, member.text);
	}

	painter.drawPerch();
	painter.drawMargin();
	painter.drawBorder();
}
